package com.sanhuo.triild.utils;

import com.sanhuo.triild.Basic.BaseMapper;
import com.sanhuo.triild.annotation.*;
import com.sanhuo.triild.provider.BaseProvider;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;


public class AnnotationUtil {

    /**
     * 判断是否是Mapper注解的类
     *
     * @param clazz
     * @return
     */
    public static boolean isMapper(Class clazz) {
        Annotation annotation = clazz.getAnnotation(Mapper.class);
        return annotation != null;
    }

    /**
     * 判断是否有@Result注解及获取其注解
     *
     * @param method
     * @return
     */
    public static boolean haveResult(Method method) {
        Results results = method.getDeclaredAnnotation(Results.class);
        return results != null && results.value().length > 0;
    }

    public static <T> Results getResultsAnnotation(Method method) {
        return method.getDeclaredAnnotation(Results.class);
    }

    /**
     * 判断是否是CRUD注解已经获取对应的注解
     *
     * @param method
     * @param <T>
     * @return
     */
    public static <T> boolean isSelect(Method method) {
        Annotation select = method.getAnnotation(Select.class);
        Annotation selectProvider = method.getAnnotation(SelectProvider.class);
        return select != null || selectProvider != null;
    }

    public static Select getSelectAnnotation(Method method) {
        return method.getAnnotation(Select.class);
    }

    public static SelectProvider getSelectProviderAnnotation(Method method) {
        return method.getAnnotation(SelectProvider.class);
    }

    public static <T> boolean isUpdate(Method method) {
        Annotation update = method.getAnnotation(Update.class);
        Annotation updateProvider = method.getAnnotation(UpdateProvider.class);
        return update != null || updateProvider != null;
    }

    public static Update getUpdateAnnotation(Method method) {
        return method.getAnnotation(Update.class);
    }

    public static UpdateProvider getUpdateProviderAnnotation(Method method) {
        return method.getAnnotation(UpdateProvider.class);
    }

    public static <T> boolean isInsert(Method method) {
        Annotation insert = method.getAnnotation(Insert.class);
        Annotation insertProvider = method.getAnnotation(InsertProvider.class);
        return insert != null || insertProvider != null;
    }

    public static Insert getInsertAnnotation(Method method) {
        return method.getAnnotation(Insert.class);
    }

    public static InsertProvider getInsertProviderAnnotation(Method method) {
        return method.getAnnotation(InsertProvider.class);
    }

    public static <T> boolean isDelete(Method method) {
        Annotation delete = method.getAnnotation(Delete.class);
        Annotation deleteProvider = method.getAnnotation(DeleteProvider.class);
        return delete != null || deleteProvider != null;
    }

    public static Delete getDeleteAnnotation(Method method) {
        return method.getAnnotation(Delete.class);
    }

    public static DeleteProvider getDeleteProviderAnnotation(Method method) {
        return method.getAnnotation(DeleteProvider.class);
    }

    public static String getTableName(Class clazz) {
        Annotation annotation = clazz.getAnnotation(Table.class);
        if (annotation != null) {
            return ((Table) annotation).value();
        } else {
            return clazz.getSimpleName().toLowerCase();
        }
    }

    /**
     * 判断是不是CRUD注解的方法
     *
     * @param method
     * @return
     */
    public static boolean isDaoMethod(Method method) {
        return isSelect(method) || isDelete(method) || isInsert(method) || isUpdate(method);
    }

    public static boolean isBaseMapper(Method method) {
        return BaseMapper.class.equals(method.getDeclaringClass());
    }

    /**
     * 获取provider注解所对应的sql语句
     *
     * @param method
     * @param args
     * @return
     */
    public static String providerBuilder(Method method, Object[] args, Type type) {
        Class providerClass = null;
        Method providerMethod = null;
        String sql = null;
        try {
            if (isSelect(method)) {
                SelectProvider selectProvider = getSelectProviderAnnotation(method);
                providerClass = selectProvider.type();
                if (BaseProvider.class.getSimpleName().toLowerCase().equals(providerClass.getSimpleName().toLowerCase())) {
                    Constructor constructor = BaseProvider.class.getDeclaredConstructor(String.class);
                    constructor.setAccessible(true);
                    Class tclass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
                    String tableName = getTableName(tclass);
                    BaseProvider baseProvider = (BaseProvider) constructor.newInstance(tableName);
                    providerMethod = BaseProvider.class.getDeclaredMethod(selectProvider.method());
                    providerMethod.setAccessible(true);
                    sql = (String) providerMethod.invoke(baseProvider);
                } else {
                    providerMethod = providerClass.getDeclaredMethod(selectProvider.method(), args[0].getClass());
                    sql = (String) providerMethod.invoke(providerClass.newInstance(), args[0]);
                }


            } else if (isUpdate(method)) {
                UpdateProvider updateProvider = getUpdateProviderAnnotation(method);
                providerClass = updateProvider.type();
                if (BaseProvider.class.getSimpleName().toLowerCase().equals(providerClass.getSimpleName().toLowerCase())) {
                    Constructor constructor = BaseProvider.class.getDeclaredConstructor(String.class);
                    constructor.setAccessible(true);
                    Class tclass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
                    String tableName = getTableName(tclass);
                    BaseProvider baseProvider = (BaseProvider) constructor.newInstance(tableName);
                    providerMethod = BaseProvider.class.getDeclaredMethod(updateProvider.method(), Object.class);
                    providerMethod.setAccessible(true);
                    sql = (String) providerMethod.invoke(baseProvider, args[0]);

                } else {
                    providerMethod = providerClass.getDeclaredMethod(updateProvider.method(), args[0].getClass());
                    sql = (String) providerMethod.invoke(providerClass.newInstance(), args[0]);
                }

            } else if (isInsert(method)) {
                InsertProvider insertProvider = getInsertProviderAnnotation(method);
                providerClass = insertProvider.type();
                if (BaseProvider.class.getSimpleName().toLowerCase().equals(providerClass.getSimpleName().toLowerCase())) {
                    Constructor constructor = BaseProvider.class.getDeclaredConstructor(String.class);
                    constructor.setAccessible(true);
                    Class tclass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
                    String tableName = getTableName(tclass);
                    BaseProvider baseProvider = (BaseProvider) constructor.newInstance(tableName);
                    providerMethod = BaseProvider.class.getDeclaredMethod(insertProvider.method(), Object.class);
                    providerMethod.setAccessible(true);
                    sql = (String) providerMethod.invoke(baseProvider, args[0]);
                } else {
                    providerMethod = providerClass.getDeclaredMethod(insertProvider.method(), args[0].getClass());
                    sql = (String) providerMethod.invoke(providerClass.newInstance(), args[0]);
                }
            } else if (isDelete(method)) {
                DeleteProvider deleteProvider = getDeleteProviderAnnotation(method);
                providerClass = deleteProvider.type();
                if (BaseProvider.class.getSimpleName().toLowerCase().equals(providerClass.getSimpleName().toLowerCase())) {
                    Constructor constructor = BaseProvider.class.getDeclaredConstructor(String.class);
                    constructor.setAccessible(true);
                    Class tclass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
                    String tableName = getTableName(tclass);
                    BaseProvider baseProvider = (BaseProvider) constructor.newInstance(tableName);
                    providerMethod = BaseProvider.class.getDeclaredMethod(deleteProvider.method(), String.class);
                    providerMethod.setAccessible(true);
                    sql = (String) providerMethod.invoke(baseProvider, args[0]);
                } else {
                    providerMethod = providerClass.getDeclaredMethod(deleteProvider.method(), args[0].getClass());
                    sql = (String) providerMethod.invoke(providerClass.newInstance(), args[0]);
                }
            }

        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return sql;
    }

}
